package cn.reinforce.util.http;

import cn.reinforce.util.http.exception.NetworkException;
import cn.reinforce.util.http.model.HttpResult;
import cn.reinforce.util.image.ImageUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 模拟http请求的工具类
 * 支持get，post，put，delete
 *
 * @author 幻幻Fate
 * @date 2016-09-06
 * @since 1.0.0
 */
public class HttpClientUtil2 {

    private static final Logger LOG = LoggerFactory.getLogger(HttpClientUtil2.class);

    public static final String ENCODE = "UTF-8";

    private HttpClientUtil2() {
        super();
    }

    public static HttpResult get(String url, Map<String, String> headers, String cookie) {
        HttpClient client = createClient();
        HttpRequest.Builder builder = HttpRequest.newBuilder()
                .uri(URI.create(checkUrl(url)))
                .header("cookie", cookie)
                .header("Content-Type", "text/html;charset=UTF-8")
                .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36");

        if (null != headers) {
            for (String name : headers.keySet()) {
                builder.header(name, headers.get(name));
            }
        }

        HttpRequest request = builder.GET().build();

        HttpResponse<String> response = null;
        try {
            response = client.send(request, HttpResponse.BodyHandlers.ofString());
        } catch (IOException e) {
            throw new NetworkException("网络异常！");
        } catch (InterruptedException e) {
            throw new NetworkException("网络中断！");
        }

        return getResult(response);
    }

    public static HttpResult get(String url, List<Cookie> cookies) {
        StringBuilder tmpCookies = new StringBuilder();
        if (cookies != null) {
            for (Cookie c : cookies) {
                tmpCookies.append(c.getName()).append("=").append(c.getValue()).append(";");
            }
        }

        return get(url, null, tmpCookies.toString());
    }

    public static HttpResult get(String url, Map<String, String> headers, List<Cookie> cookies) {
        StringBuilder tmpCookies = new StringBuilder();
        if (cookies != null) {
            for (Cookie c : cookies) {
                tmpCookies.append(c.getName()).append("=").append(c.getValue()).append(";");
            }
        }

        return get(url, headers, tmpCookies.toString());
    }

    public static HttpResult get(String url) {
        return get(url, null, "");
    }

    public static HttpResult post(String url, Map<String, Object> data, List<Cookie> cookies) {
        StringBuilder tmpcookies = new StringBuilder();
        if (cookies != null) {
            for (Cookie c : cookies) {
                tmpcookies.append(c.getName()).append("=").append(c.getValue()).append(";");
            }
        }
        return post(url, data, null, tmpcookies.toString());
    }


    public static HttpResult post(String url, Map<String, Object> data, String cookie) {
        return post(url, data, null, cookie);
    }

    public static HttpResult post(String url, Map<String, Object> data, Map<String, String> headers) {
        return post(url, data, headers, null);
    }

    public static HttpResult post(String url, Map<String, Object> data, Map<String, String> headers, String cookie) {
        if(headers == null){
            headers = new HashMap<>();
            headers.put("Content-Type","application/x-www-form-urlencoded");
        }

        return postByEntity(url, allParamToString(data), headers, cookie);
    }

    public static HttpResult postByEntity(String url, String stringEntity) {
        return postByEntity(url, stringEntity, null, null);
    }

    public static HttpResult postByEntity(String url, String stringEntity, Map<String, String> headers) {
        return postByEntity(url, stringEntity, headers, null);
    }

    public static HttpResult postByEntity(String url, String stringEntity, Map<String, String> headers, String cookie) {
        HttpClient client = createClient();
        HttpRequest.Builder builder = HttpRequest.newBuilder()
                .uri(URI.create(checkUrl(url)));

        if(headers == null || !headers.containsKey("User-Agent")){
            builder.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36");
        }

        if(headers == null || !headers.containsKey("Content-Type")){
            builder.header("Content-Type", "text/html;charset=UTF-8");
        }

        if (null != headers) {
            for (String name : headers.keySet()) {
                builder.header(name, headers.get(name));
            }
        }

        if(StringUtils.isNotEmpty(cookie)){
            builder.header("cookie", cookie);
        }

        HttpRequest request = builder
                .POST(HttpRequest.BodyPublishers.ofString(stringEntity))
                .build();

        HttpResponse<String> response = null;
        try {
            response = client.send(request, HttpResponse.BodyHandlers.ofString());
        } catch (IOException e) {
            throw new NetworkException("网络异常！");
        } catch (InterruptedException e) {
            throw new NetworkException("网络中断！");
        }

        return getResult(response);
    }

    /**
     * 模拟表单上传
     */
//    public static String multipartRequest(String url, Map<String, String> parameters) {
//        MultipartEntityBuilder multipartEntity = MultipartEntityBuilder.create();
//        ContentType contentType = ContentType.create(HTTP.PLAIN_TEXT_TYPE, HTTP.UTF_8);
//        for (Map.Entry<String, String> entry : parameters.entrySet()) {
//            multipartEntity.addTextBody(entry.getKey(), entry.getValue(), contentType);
//        }
//        // Now write the image
//        String fullFilePath = parameters.get("file_path");
//        if (!StringUtils.isEmpty(fullFilePath)) {
//            multipartEntity.addPart("uploadFile", new FileBody(new File(fullFilePath)));
//        }
//
//        CloseableHttpClient client = HttpClientBuilder.create().build();
//        HttpPost post = new HttpPost(url);
//
//
//        post.setEntity(multipartEntity.build());
//        HttpResponse response = null;
//        try {
//            response = client.execute(post);
//            String submitResult = EntityUtils.toString(response.getEntity(), ENCODE);
//
//            return submitResult;
//        } catch (IOException e) {
//            LOG.error(e.getMessage(), e);
//        } finally {
//            try {
//                client.close();
//            } catch (IOException e) {
//                LOG.error(e.getMessage(), e);
//            }
//        }
//        return null;
//    }


    public static HttpResult put(String url, Map<String, Object> data) {

        HttpClient client = createClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(checkUrl(url)))
                .header("Content-Type", "text/html;charset=UTF-8")
                .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36")
                .PUT(HttpRequest.BodyPublishers.ofString(allParamToString(data)))
                .build();

        HttpResponse<String> response = null;
        try {
            response = client.send(request, HttpResponse.BodyHandlers.ofString());
        } catch (IOException e) {
            throw new NetworkException("网络异常！");
        } catch (InterruptedException e) {
            throw new NetworkException("网络中断！");
        }

        return getResult(response);
    }

    public static HttpResult delete(String url) {
        HttpClient client = createClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(checkUrl(url)))
                .header("Content-Type", "text/html;charset=UTF-8")
                .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36")
                .DELETE()
                .build();

        HttpResponse<String> response = null;
        try {
            response = client.send(request, HttpResponse.BodyHandlers.ofString());
        } catch (IOException e) {
            throw new NetworkException("网络异常！");
        } catch (InterruptedException e) {
            throw new NetworkException("网络中断！");
        }

        return getResult(response);
    }

    /**
     * 下载图片到本地
     *
     * @param url
     * @return
     */
    public static String downloadImg(String url, String folder) {
        HttpClient client = createClient();
        String filename = url.substring(url.lastIndexOf("/") + 1);
        String fileType = url.substring(url.lastIndexOf(".") + 1);
        if (StringUtils.isEmpty(fileType) || !ImageUtil.isImage(fileType)) {
            fileType = "jpg";
        }
        File dir = new File(folder);
        if (!dir.exists()) {
            dir.mkdirs();
        }

        Path storePath = Path.of(folder + File.separator + filename);
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(checkUrl(url)))
                .header("Content-Type", "text/html;charset=UTF-8")
                .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36")
                .GET()
                .build();

        HttpResponse<Path> response = null;
        try {
            response = client.send(request, HttpResponse.BodyHandlers.ofFile(storePath));
        } catch (IOException e) {
            throw new NetworkException("网络异常！");
        } catch (InterruptedException e) {
            throw new NetworkException("网络中断！");
        }
        return response.body().toString();
    }

    public static HttpClient createClient() {
        return HttpClient.newBuilder()
                .version(HttpClient.Version.HTTP_1_1)
//                .followRedirects(HttpClient.Redirect.NORMAL)
                .connectTimeout(Duration.ofSeconds(15))
                .build();
    }

    public static HttpClient createHttp2Client() {
        return HttpClient.newBuilder()
                .version(HttpClient.Version.HTTP_2)
//                .followRedirects(HttpClient.Redirect.NORMAL)
                .connectTimeout(Duration.ofSeconds(15))
                .build();
    }

    public static HttpResult getResult(HttpResponse<String> response) {
        HttpResult result = new HttpResult();
        result.setStatusCode(response.statusCode());
        result.setResult(response.body());
        return result;
    }

    public static String checkUrl(String url) {
        if (!StringUtils.startsWith(url, "https://") && !StringUtils.startsWith(url, "http://")) {
            url = "http://" + url;
        }
        return url;
    }

    /**
     * 所有参数转换成一个字符串
     *
     * @param request
     * @return
     */
    public static String commonParamToString(HttpServletRequest request) {
        StringBuilder sb = new StringBuilder();
        List<String> paramNames = Collections.list(request.getParameterNames());

        paramNames.sort(Comparator.comparing(String::toLowerCase));

        for (String paramName : paramNames) {
            if (!"signature" .equals(paramName)) {
                String[] paramValues = request.getParameterValues(paramName);
                for (String s : paramValues) {
                    sb.append(paramName).append("=").append(s).append("&");
                }
            }
        }
        return sb.length() == 0 ? "" : sb.substring(0, sb.length() - 1);
    }

    public static String allParamToString(Map<String, Object> data) {
        StringBuilder sb = new StringBuilder();
        for (String key : data.keySet()) {
            sb.append(key).append("=").append(data.get(key)).append("&");
        }
        return sb.length() == 0 ? "" : sb.substring(0, sb.length() - 1);
    }
}
